package com.ctfo.ulcp.exp.db

import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.javatime.datetime
import java.time.Duration
import java.time.LocalDateTime


object Tasks : Table("t_resource_task") {

    val id = integer("ID").autoIncrement()
    val resourceId = integer("RESOURCE_ID")
    val startTime = datetime("START_TIME")
    private val endTime = datetime("END_TIME").nullable()
    private val heartbeat = datetime("HEARTBEAT").nullable()
    private val errorCause = text("ERROR_CAUSE").nullable()
    val status = varchar("STATUS", length = 255)

    override val primaryKey = PrimaryKey(id)


    fun createTask(resourceId: Int): Int {
        return Tasks.insert {
            it[Tasks.resourceId] = resourceId
            it[startTime] = LocalDateTime.now()
            it[heartbeat] = LocalDateTime.now()
            it[status] = TaskStatus.Pending.name
        }[id]
    }

    fun saveTaskHeartbeat(taskId: Int) {
        Tasks.update({ id eq taskId }) {
            it[heartbeat] = LocalDateTime.now()
        }
    }


    fun updatePendingToFailed(duration: Duration): Int {
        // 当前时间 - 最后活跃时间 >= duration
        val lastActiveTime = LocalDateTime.now().minusSeconds(duration.toSeconds())
        val taskIds = Tasks.selectAll().where {
            (heartbeat less lastActiveTime).and { status eq TaskStatus.Pending.name }
        }.forUpdate().map { it[id] }

        taskIds.forEach {
            Tasks.update({ id eq it }) {
                it[status] = TaskStatus.Failed.name
                it[errorCause] = "任务超时"
                it[endTime] = LocalDateTime.now()
            }
        }
        return taskIds.size
    }


    fun updateToSucceeded(taskId: Int) {
        Tasks.update({ id eq taskId }) {
            it[status] = TaskStatus.Succeeded.name
            it[endTime] = LocalDateTime.now()
        }
    }

    fun updateToFailed(taskId: Int, err: Throwable) {
        Tasks.update({ id eq taskId }) {
            it[status] = TaskStatus.Failed.name
            it[errorCause] = err.stackTraceToString()
            it[endTime] = LocalDateTime.now()
        }
    }

}